package it.unibo.imola.parser.visitor.entity;

import java.util.ArrayList;

import org.apache.commons.lang3.ArrayUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class CallStatement extends Statement {

	private Logger logger = LoggerFactory.getLogger(getClass());
	protected String name;
	protected ToolBox toolbox;
	protected ArrayList<Object> actualParametersList;
	protected Class<?>[] actualParameterType;
	protected Object[] unboxedActualParameters;

	public CallStatement(String name, ToolBox toolbox) {
		if (name == null)
			throw new IllegalArgumentException("name == null");
		if (toolbox == null)
			throw new IllegalArgumentException("toolbox == null");
		this.name = name;
		this.toolbox = toolbox;
		actualParametersList = new ArrayList<Object>(0);
	}

	public void addActualParameter(Object param) {
		if (param == null)
			throw new IllegalArgumentException("param == null");
		actualParametersList.add(param);
	}

	@Override
	public boolean check(GlobalEnvironment gEnv, Environment env) throws Exception {
		super.check(gEnv,env);

		actualParameterType = new Class<?>[actualParametersList.size()];

		// creo l'array di tipi per vedere se esiste un metodo con questa
		// signature
		// cioè se corrisponde alla definizione dei parametri formali
		//
		// devo cercare sia tra le funzioni che ho definito, sia nella toolbox

		for (int i = 0; i < actualParameterType.length; i++) {
			if (actualParametersList.get(i) instanceof String) {
				String parameter = actualParametersList.get(i).toString();
				if (parameter.startsWith("\"")) { // è una stringa costante
					actualParameterType[i] = String.class;
					continue;
				} else {
					if (!env.getVariables().containsKey(parameter)) {
						throw new Exception(name + ": check: Undeclared variable \"" + parameter + "\"");
					}
					Variable var = env.getVariables().get(parameter);
					actualParameterType[i] = var.getValue().getClass();
					continue;
				}

			}
			// qui ci arriva sempre se non è un nome di una variabile
			actualParameterType[i] = actualParametersList.get(i).getClass();
		}
		
		return true;

	}

	@Override
	public void exec(GlobalEnvironment gEnv, Environment env) throws Exception {
		super.exec(gEnv,env);

		// creo un array con i parametri "unboxed" quindi con solo costanti.
		// per fare questo, se il parametro attuale è una costante, resta uguale
		// se invece è una variabile, ci metto una copia del valore attuale

		unboxedActualParameters = new Object[actualParametersList.size()];

		for (int i = 0; i < unboxedActualParameters.length; i++) {
			if (actualParametersList.get(i) instanceof String) {
				String parameter = actualParametersList.get(i).toString();

				if (parameter.startsWith("\"")) { // è una stringa costante
					unboxedActualParameters[i] = parameter.substring(1, parameter.length() - 1);
				} else {
					if (!env.getVariables().containsKey(parameter)) {
						throw new Exception(name + ": exec: Undeclared variable \"" + parameter + "\"");
					}
					Variable var = env.getVariables().get(parameter);
					unboxedActualParameters[i] = var.getValue();
				}
			} else
				unboxedActualParameters[i] = actualParametersList.get(i);
		}

		// adesso ho tutti i miei parametri attuali "unboxed", e se ho fatto la
		// check() sono sicuro
		// che o nel GlobalEnv o nella ToolBox il metodo da chiamare c'è.
		// vado prima a vedere se la call fa riferimento ad una funzione
		// definita nello script

		//qui ho spostato il codice in customcallstatement
		

		// se non c'è eseguo dal toolbox
		
		//qui ho spostato il codice in builtincallstatement

	}

	@Override
	public String toString() {
		// return new ToStringCreator(getClass()).
		// append(name, actualParametersList).toString();
		return name + ArrayUtils.toString(actualParametersList);
	}
}
